﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace Common
{
    public static class ImageOperation
    {
        delegate int IntInt(int val);
        static IntInt getR = (int val) => ((val >> 16) & 0xFF);
        static IntInt getG = (int val) => ((val >> 8) & 0xFF);
        static IntInt getB = (int val) => (val & 0xFF);

        public delegate void PostProcessDelegate(ref float r, ref float g, ref float b,
                float srcR, float srcG, float srcB);
        public static float Clampf(float f, float min, float max)
        {
            if (f > max) return max;
            if (f < min) return min;
            return f;
        }
        public static Bitmap ImageMaskTransform(System.Drawing.Bitmap bitmap, float[] mask, PostProcessDelegate postProc)
        {
            Bitmap rs = new Bitmap(bitmap);
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            BitmapData destData = rs.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            float sumMask = 0.0f;
            for (int i = 0; i < 9; i++)
            {
                sumMask += Math.Abs(mask[i]);
            }
            unsafe
            {
                for (int i = 0; i < bitmap.Height; i++)
                {
                    int im1 = i - 1;
                    int ip1 = i + 1;
                    if (im1 < 0) im1 = 0;
                    if (ip1 >= bitmap.Height) ip1 = bitmap.Height - 1;
                    int* pixm1 = (int*)((int)data.Scan0 + data.Stride * im1);
                    int* pixp1 = (int*)((int)data.Scan0 + data.Stride * ip1);
                    int* pix = (int*)((int)data.Scan0 + data.Stride * i);
                    int* pixDest = (int*)((int)destData.Scan0 + destData.Stride * i);
                    for (int j = 0; j < bitmap.Width; j++)
                    {
                        int jm1 = j - 1;
                        int jp1 = j + 1;
                        if (jm1 < 0) jm1 = 0;
                        if (jp1 >= bitmap.Width) jp1 = bitmap.Width - 1;

                        float r, g, b;

                        r = getR(pixm1[jm1]) * mask[0] + getR(pixm1[j]) * mask[1] + getR(pixm1[jp1]) * mask[2] +
                            getR(pix[jm1]) * mask[3] + getR(pix[j]) * mask[4] + getR(pix[jp1]) * mask[5] +
                            getR(pixp1[jm1]) * mask[6] + getR(pixp1[j]) * mask[7] + getR(pixp1[jp1]) * mask[8];
                        r /= sumMask;

                        g = getG(pixm1[jm1]) * mask[0] + getG(pixm1[j]) * mask[1] + getG(pixm1[jp1]) * mask[2] +
                            getG(pix[jm1]) * mask[3] + getG(pix[j]) * mask[4] + getG(pix[jp1]) * mask[5] +
                            getG(pixp1[jm1]) * mask[6] + getG(pixp1[j]) * mask[7] + getG(pixp1[jp1]) * mask[8];
                        g /= sumMask;

                        b = getB(pixm1[jm1]) * mask[0] + getB(pixm1[j]) * mask[1] + getB(pixm1[jp1]) * mask[2] +
                            getB(pix[jm1]) * mask[3] + getB(pix[j]) * mask[4] + getB(pix[jp1]) * mask[5] +
                            getB(pixp1[jm1]) * mask[6] + getB(pixp1[j]) * mask[7] + getB(pixp1[jp1]) * mask[8];
                        b /= sumMask;

                        if (postProc != null)
                            postProc(ref r, ref g, ref b, getR(pix[j]), getG(pix[j]), getB(pix[j]));
                        r = Clampf(r, 0, 255);
                        g = Clampf(g, 0, 255);
                        b = Clampf(b, 0, 255);
                        pixDest[j] = Color.FromArgb((int)(r), (int)g, (int)b).ToArgb();
                    }
                }
            }
            rs.UnlockBits(destData);
            bitmap.UnlockBits(data);
            return rs;
        }

        public delegate void TransformDelegate(ref float r, ref float g, ref float b, int x, int y);
        public static Bitmap ImageSingleTransform(System.Drawing.Bitmap bitmap, TransformDelegate postProc)
        {
            Bitmap rs = new Bitmap(bitmap);
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            BitmapData destData = rs.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppRgb);

            unsafe
            {
                for (int i = 0; i < bitmap.Height; i++)
                {
                    int* pix = (int*)((int)data.Scan0 + data.Stride * i);
                    int* pixDest = (int*)((int)destData.Scan0 + destData.Stride * i);
                    for (int j = 0; j < bitmap.Width; j++)
                    {
                        float r, g, b;
                        r = getR(pix[j]);
                        g = getG(pix[j]);
                        b = getB(pix[j]);

                        postProc(ref r, ref g, ref b, j, i);
                        r = Clampf(r, 0, 255);
                        g = Clampf(g, 0, 255);
                        b = Clampf(b, 0, 255);
                        pixDest[j] = Color.FromArgb((int)(r), (int)g, (int)b).ToArgb();
                    }
                }
            }
            rs.UnlockBits(destData);
            bitmap.UnlockBits(data);
            return rs;
        }
        public delegate void MergeOperator(Color c1, Color c2, out Color rc);
        public static Bitmap MergeBitmap(Bitmap b1, Bitmap b2, MergeOperator op)
        {
            if (b1.Size != b2.Size)
                throw new Exception();
            Bitmap bitRs = new Bitmap(b1.Width, b1.Height);
            BitmapData data1, data2, data;
            data1 = b1.LockBits(new Rectangle(0, 0, b1.Width, b1.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
            data2 = b2.LockBits(new Rectangle(0, 0, b1.Width, b1.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
            data = bitRs.LockBits(new Rectangle(0, 0, b1.Width, b1.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
            unsafe
            {
                for (int i = 0; i < b1.Height; i++)
                {
                    int* pix1 = (int*)((int)data1.Scan0 + data.Stride * i);
                    int* pix2 = (int*)((int)data2.Scan0 + data.Stride * i);
                    int* pixDest = (int*)((int)data.Scan0 + data.Stride * i);
                    for (int j = 0; j < b1.Width; j++)
                    {
                        Color c1 = Color.FromArgb(pix1[j]);
                        Color c2 = Color.FromArgb(pix2[j]);
                        Color cr;
                        op(c1, c2, out cr);
                        pixDest[j] = cr.ToArgb();
                    }
                }
            }
            b1.UnlockBits(data1);
            b2.UnlockBits(data2);
            bitRs.UnlockBits(data);
            return bitRs;
        }
    }
}
